/****************************************************************************
 * semidrive/chips/e3650/e3650_irq.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <assert.h>
#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/irq.h>

#include "arm_internal.h"

#include "arm_gic.h"

#define GICV3_PRIO_SHIF 3UL

#define INTSTACK_ALLOC (CONFIG_NR_CPUS * INTSTACK_SIZE)

#if (defined(CONFIG_SMP) || defined(CONFIG_BMP)) && CONFIG_ARCH_INTERRUPTSTACK > 7
/* In the SMP configuration, we will need custom IRQ and FIQ stacks.
 * These definitions provide the aligned stack allocations.
 */

static uint64_t g_irqstack_alloc[INTSTACK_ALLOC >> 3];
static uint64_t g_fiqstack_alloc[INTSTACK_ALLOC >> 3];

/* These are arrays that point to the top of each interrupt stack */

const uintptr_t g_irqstack_top[CONFIG_NR_CPUS] = { (uintptr_t)g_irqstack_alloc + INTSTACK_SIZE,
#if CONFIG_NR_CPUS > 1
						   (uintptr_t)g_irqstack_alloc + (2 * INTSTACK_SIZE),
#endif
#if CONFIG_NR_CPUS > 2
						   (uintptr_t)g_irqstack_alloc + (3 * INTSTACK_SIZE),
#endif
#if CONFIG_NR_CPUS > 3
						   (uintptr_t)g_irqstack_alloc + (4 * INTSTACK_SIZE)
#endif
};

const uintptr_t g_fiqstack_top[CONFIG_NR_CPUS] = { (uintptr_t)g_fiqstack_alloc + INTSTACK_SIZE,
#if CONFIG_NR_CPUS > 1
						   (uintptr_t)g_fiqstack_alloc + 2 * INTSTACK_SIZE,
#endif
#if CONFIG_NR_CPUS > 2
						   (uintptr_t)g_fiqstack_alloc + 3 * INTSTACK_SIZE,
#endif
#if CONFIG_NR_CPUS > 3
						   (uintptr_t)g_fiqstack_alloc + 4 * INTSTACK_SIZE
#endif
};

#endif

/****************************************************************************
 * Name: up_get_intstackbase
 *
 * Description:
 *   Return a pointer to the "alloc" the correct interrupt stack allocation
 *   for the current CPU.
 *
 ****************************************************************************/

#if (defined(CONFIG_SMP) || defined(CONFIG_BMP)) && CONFIG_ARCH_INTERRUPTSTACK > 7
uintptr_t up_get_intstackbase(int cpu)
{
	return g_irqstack_top[cpu] - INTSTACK_SIZE;
}
#endif

/****************************************************************************
 * Name: up_init_irq
 *
 * Description:
 *   Init the IRQ specified by 'irq'
 *
 ****************************************************************************/

void up_init_irq(int irq, int irq_prio)
{
#ifdef CONFIG_ARCH_IRQPRIO
	up_prioritize_irq(irq, irq_prio << GICV3_PRIO_SHIF);
#endif
}

/****************************************************************************
 * Name: up_irq_is_enabled
 *
 * Description:
 *   Determine if an IRQ is enabled.
 *
 ****************************************************************************/

bool up_irq_is_enabled(int irq)
{
	return arm_gic_irq_is_enabled(irq);
}

/****************************************************************************
 * Name: up_clear_irq
 *
 * Description:
 *   Clear the pending IRQ.
 *
 ****************************************************************************/

void up_clear_irq(int irq)
{
	uint32_t index;
	uint32_t offset;
	uint32_t base;

	if (GIC_IS_SPI(irq)) {
		index = irq / GIC_NUM_INTR_PER_REG;
		offset = irq % GIC_NUM_INTR_PER_REG;
		modifyreg32((volatile uint32_t *)ICPENDR(CONFIG_GICD_BASE, index),
				 0, (1 << offset));
	} else {
		base = CONFIG_GICR_BASE + up_cpu_index() * CONFIG_GICR_OFFSET + GICR_SGI_BASE_OFF;
		modifyreg32((volatile uint32_t *)ICPENDR(base, 0), 0, (1 << irq));
	}
}
